home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / Timing / clock1_2.lha / clock1_2 / compute_date.c < prev    next >
C/C++ Source or Header  |  1994-05-16  |  6KB  |  165 lines

  1. /* This code comes from a posting by Tomas Rokicki to the old
  2.  * comp.sys.amiga newsgroup in August of 1988.  Since it was publicly
  3.  * posted and there is no copyright, I'm assuming it's ok to use this
  4.  * code.  This is pretty clever stuff!
  5.  *
  6.  * I've renamed this function to compute_date(), and made it return
  7.  * the year, month, day, and weekday number, rather than setting
  8.  * globals.  I've also modified the code a little to do this, and to
  9.  * use 0-based day, month, and weekday, and to compute day of year.
  10.  * If it doesn't work, it's almost certainly my fault.
  11.  *
  12.  *
  13.  * Here's Tomas' original opening comment:
  14.  *
  15.  *
  16.  *   Written by Tomas Rokicki of Radical Eye Software.
  17.  *
  18.  *   Here's an updated ShowDate() routine, as requested by Rob
  19.  *   Peck.  This one even has some limited explanations of how
  20.  *   it works!  It sets the day of week as well as month, day,
  21.  *   and year from the values returned by DateStamp().
  22.  *
  23.  *   Note that the original code published in Peck's book, would
  24.  *   fail for dates prior to 1984.  When compiled with 16-bit
  25.  *   integers, it would fail for dates after 2007.  The current
  26.  *   code will work with any size integer from Amiga day 0 until
  27.  *   March 1, 2100.
  28.  */
  29.  
  30. #ifndef OLDTIME
  31.  
  32. /*
  33.  *   This routine returns in *m, *d, *y, *w, and *jul the current
  34.  *   month, day, year, day of week, and day of year.  Months, days,
  35.  *   and day of weeks all start at 1 with first members January, the
  36.  *   first, and Sunday, respectively.
  37.  */
  38. void compute_date(long n, int *y, int *m, int *d, int *w, int *jul)
  39. {
  40.     int tmp_y, tmp_m, ly;
  41. /*
  42.  *   Set n to the number of days since Amiga day -671, which is
  43.  *   March 1, 1976.  It's easier to figure years starting in March,
  44.  *   since then the lengths of the months are 31, 30, 31, 30, 31,
  45.  *   31, 30, 31, 30, 31, 31, 28.  This is almost linear.
  46.  */
  47.    n += 671 ;
  48. /*
  49.  *   The easiest is the weekday.  This is simply the number of days
  50.  *   modulo 7, corrected for the start date.  March 1, 1976 was a
  51.  *   Monday, so we add 1 to get back to a Sunday, and take the modulo.
  52.  */
  53.    *w = (n + 1) % 7 ;
  54. /*
  55.  *   There are exactly 1461 days every four years, until 2100, which
  56.  *   is the first year divisible by 4 that is not a leap year AA
  57.  *   (After Amiga.)  This gives the years lengths of 365 (1976),
  58.  *   365 (1977), 365 (1978), and 366 (1979).  Note that this is
  59.  *   correct because we start our years in March, so 1979 is the
  60.  *   leap year.
  61.  */
  62.    tmp_y = (4 * n + 3) / 1461 ;
  63. /*
  64.  *   We now subtract off the years (see them melt off her face.)
  65.  *   We use a long constant for 16-bit systems.  Again we use the
  66.  *   fact that the leap year is the fourth year, not the first.
  67.  */
  68.    n -= 1461L * tmp_y / 4 ;
  69. /*
  70.  *   Now we can adjust the year to the proper value by adding
  71.  *   1976.
  72.  */
  73.    tmp_y += 1976 ;
  74. /*
  75.  * We're now in a position to calculate the day of the year.  At this
  76.  * point, n is the day of the year with 1 March = 0.  To get the
  77.  * actual day of year, we need to add the number of days in Jan and
  78.  * Feb, and take this modulo the number of days in that year.  There
  79.  * are 59 days in Jan and Feb, 60 in leap years, and 365 days per
  80.  * year, 366 in leap years.  So lets figure out if this is a leap year
  81.  * first.  (There is a catch here:  our idea of the year may be wrong.
  82.  * Looking below, you see we increment the year if the month is Jan or
  83.  * Feb.  Actually, though, that's what we want, because we only add
  84.  * the extra day after Feb in a leap year.)
  85.  */
  86.    ly = (tmp_y%4) == 0;
  87.    *jul = (n + 59 + ly) % (365 + ly);
  88. /*
  89.  *   We calculate the month.  Since we start in March, the length
  90.  *   of the months are always 30 or 31, except for the last month,
  91.  *   which is shorter.  This is fortunate, as it allows us to use
  92.  *   a simple mathematical formula for the month.  The lengths of
  93.  *   the months are (31, 30, 31, 30, 31), repeated three times and
  94.  *   the end lopped off.  So, our slope is 153/5.  An intercept of
  95.  *   2 gives us the 31 and 30 lengths.
  96.  */
  97.    tmp_m = (5 * n + 2) / 153 ;
  98. /*
  99.  *   And now we subtract off the months.
  100.  */
  101.    *d = n - (153 * tmp_m + 2) / 5 ;
  102. /*
  103.  *   Now we convert from March-based years back to January-based
  104.  *   years.
  105.  */
  106.    tmp_m += 2 ;
  107. /*
  108.  *   And, if we've gone over 12, we increment the year.
  109.  */
  110.    if (tmp_m > 11) {
  111.       tmp_y++ ;
  112.       tmp_m -= 12 ;
  113.    }
  114. /*
  115.  *   That's it!
  116.  */
  117.    *y = tmp_y;
  118.    *m = tmp_m;
  119. }
  120.  
  121.  
  122. #else
  123. /* Here's my old implementation.  It's easier to understand, but
  124.  * bigger and slower.
  125.  */
  126.  
  127. #define FOUR_YEARS (4*365+1)
  128. /* Jan 1 is day 0, so Feb 29 is day 31+28 = 59 */
  129. #define LEAP_DAY 59
  130. /* weekday (Sunday=0) of 1-Jan-76 */
  131. #define WKDAY_BIAS 4
  132.  
  133. void compute_date(long n, int *y, int *m, int *d, int *w, int *jul)
  134.     {
  135.     /* unsigned shorts are cheaper for maths than ints */
  136.     unsigned short days = n + 365*2 + 1;
  137.     unsigned short yr4 = days/FOUR_YEARS;
  138.     unsigned short dy4 = days%FOUR_YEARS;
  139.     unsigned short dy;
  140.     
  141.     *w = (days+WKDAY_BIAS)%7;    /* easy */
  142.     if (dy4 == LEAP_DAY) {
  143.         *jul = dy4;
  144.         *d = 28;    /* 29 Feb, 0 origin */
  145.         *m = 1;    /* February, 0 origin */
  146.         *y = 1976 + 4*yr4;
  147.     } else {
  148.         static unsigned char days_in_month[12] = {
  149.         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  150.           };
  151.         unsigned char *ptr;
  152.         
  153.         if (dy4 > LEAP_DAY) --dy4;
  154.         /* now we can forget 29 Feb ever happens */
  155.         *y = 1976 + (dy4/365) + (yr4*4);
  156.         *jul = dy = dy4 % 365;
  157.         /* dy is now day of year */
  158.         
  159.         for (ptr=days_in_month; dy>=*ptr; ++ptr) dy -= *ptr;
  160.         *m = ptr-days_in_month;
  161.         *d = dy;
  162.     }
  163.     }
  164. #endif
  165.